﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GLWrap.SceneObjects;
using ReAnimation.Domain;
using GLWrap;
using GLWrap.SceneObjects.Line;

namespace ReAnimation.Graphics
{
    public class LinkSceneObjectsFactory: ILinkSceneObjectsFactory
    {
        private delegate SceneObject CreateLinkSceneObjectDelegate(Link link);
        private delegate bool CheckLinkSceneObject(SceneObject sceneObject);

        private IDictionary<MoleculeLinkType, CreateLinkSceneObjectDelegate> _linkSceneObjectsCreators;
        private IDictionary<MoleculeLinkType, CheckLinkSceneObject> _linkSceneObjectsCheckers;
        private static readonly float DefaultWeight = 0.002f;
        private static readonly float DefaultDistance = 0.007f;

        public LinkSceneObjectsFactory()
        {
            _linkSceneObjectsCreators = new Dictionary<MoleculeLinkType, CreateLinkSceneObjectDelegate>();
            _linkSceneObjectsCheckers = new Dictionary<MoleculeLinkType, CheckLinkSceneObject>();

            _linkSceneObjectsCreators.Add(MoleculeLinkType.Single, CreateSingleTypeLinkSceneObject);
            _linkSceneObjectsCreators.Add(MoleculeLinkType.Double, CreateDoubleTypeLinkSceneObject);
            _linkSceneObjectsCreators.Add(MoleculeLinkType.Hydrogen, CreateHydrogenTypeLinkSceneObject);
            _linkSceneObjectsCreators.Add(MoleculeLinkType.Sesquialteral, CreateSesquialteralTypeLinkSceneObject);
            _linkSceneObjectsCreators.Add(MoleculeLinkType.Triple, CreateTripleTypeLinkSceneObject);


            _linkSceneObjectsCheckers.Add(MoleculeLinkType.Single, SingleTypeLinkSceneObjectChecker);
            _linkSceneObjectsCheckers.Add(MoleculeLinkType.Double, DoubleTypeLinkSceneObjectChecker);
            _linkSceneObjectsCheckers.Add(MoleculeLinkType.Triple, TripleTypeLinkSceneObjectChecker);
            _linkSceneObjectsCheckers.Add(MoleculeLinkType.Hydrogen, HydrogenTypeLinkSceneObjectChecker);
            _linkSceneObjectsCheckers.Add(MoleculeLinkType.Sesquialteral, SesquialteralTypeLinkSceneObjectChecker);
        }

        #region ILinkSceneObjectsFactory Members

        public Material DefaultMaterial { get; set; }

        public SceneObject GetSceneObjectForLinkType(Link link)
        {
           return  _linkSceneObjectsCreators[link.LinkType].Invoke(link);
        }

        public bool CheckSceneObjectIsLinkType(MoleculeLinkType linkType,SceneObject sceneObject)
        {
            return _linkSceneObjectsCheckers[linkType].Invoke(sceneObject);
        }

        #endregion

        private SceneObject CreateTripleTypeLinkSceneObject(Link link)
        {
            DoubleLine doubleLine = new DoubleLine()
            {
                Distance = (float)(DefaultDistance * 2.0 / 3.0)
            };
            return new DoubleLine(new SimpleLine(), doubleLine)
            {
                Weight = DefaultWeight,
                Material = DefaultMaterial,
                PointA = link.Atom1.Position,
                PointB = link.Atom2.Position,
                Distance = DefaultDistance
            };
        }

        private SceneObject CreateSesquialteralTypeLinkSceneObject(Link link)
        {
            return new DoubleLine(new SimpleLine(), new DashedLine() { FillPercent = 60 })
            {
                Weight = DefaultWeight,
                Material = DefaultMaterial,
                PointA = link.Atom1.Position,
                PointB = link.Atom2.Position,
                Distance = DefaultDistance
            };
        }

        private SceneObject CreateHydrogenTypeLinkSceneObject(Link link)
        {
            return new DottedLine(3)
            {
                FillPercent = 30,
                Weight = DefaultWeight * 2,
                Material = DefaultMaterial,
                PointA = link.Atom1.Position,
                PointB = link.Atom2.Position
            };
        }

        private SceneObject CreateSingleTypeLinkSceneObject(Link link)
        {
            return new SimpleLine
            {
                Material = DefaultMaterial,
                PointA = link.Atom1.Position,
                PointB = link.Atom2.Position,
                Weight = DefaultWeight
            };
        }

        private SceneObject CreateDoubleTypeLinkSceneObject(Link link)
        {
            //TODO: remove constansts
            var line = new DoubleLine();
            line.PointA = link.Atom1.Position;
            line.PointB = link.Atom2.Position;
            line.Distance = DefaultDistance;
            line.Weight = DefaultWeight;
            line.Material = DefaultMaterial;

            return line;
        }

        private bool SingleTypeLinkSceneObjectChecker(SceneObject sceneObject)
        {
            return sceneObject is SimpleLine;
        }

        private bool DoubleTypeLinkSceneObjectChecker(SceneObject sceneObject)
        {
            var doubleLine = sceneObject as DoubleLine;
            if (doubleLine == null) return false;
            return doubleLine.Line1 is SimpleLine && doubleLine.Line2 is SimpleLine;
        }

        private bool TripleTypeLinkSceneObjectChecker(SceneObject sceneObject)
        {
            var doubleLine = sceneObject as DoubleLine;
            if (doubleLine == null) return false;
            return doubleLine.Line1 is SimpleLine && doubleLine.Line2 is DoubleLine;
        }

        private bool HydrogenTypeLinkSceneObjectChecker(SceneObject sceneObject)
        {
            return sceneObject is DottedLine;
        }

        private bool SesquialteralTypeLinkSceneObjectChecker(SceneObject sceneObject)
        {
            var doubleLine = sceneObject as DoubleLine;
            if (doubleLine == null) return false;
            return doubleLine.Line1 is SimpleLine && doubleLine.Line2 is DashedLine;
        }
    }
}
